package ca.tatham.senssim.diffusion;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import ca.tatham.senssim.SensorNode;
import ca.tatham.senssim.SensorType;
import ca.tatham.senssim.diffusion.GradientCache.GradientList;
import ca.tatham.senssim.util.Address;

public class SensorNodeDirectedDiffusion extends SimpleDiffusionNode implements
    SensorNode
{
  private final Set<SensorType> m_sensors = new HashSet<SensorType>();
  private int m_sourcePackets;
  private final Map<SensorType, Gradient> m_fastestGradient = new HashMap<SensorType, Gradient>();

  protected SensorNodeDirectedDiffusion(Address address, int power)
  {
    super(address, power);
    addSensorType(AbstractSinkNode.SENSOR_TYPE);
  }

  @Override
  public boolean isSource()
  {
    return true;
  }

  @Override
  public boolean isSink()
  {
    return false;
  }

  public void addSensorType(SensorType type)
  {
    m_sensors.add(type);
  }

  @Override
  protected void processInterestMessage(InterestMessage packet)
  {
    super.processInterestMessage(packet);
    m_fastestGradient.put(packet.getInterest().type, getFastestGradient(packet
        .getInterest().type));
  }

  @Override
  public void tick()
  {
    super.tick();
    for (SensorType sensor : m_sensors)
    {
      Gradient fastestGradient = m_fastestGradient.get(sensor);
      if (fastestGradient != null)
      {
        if (fastestGradient.shouldSend())
        {
          GradientList gradients = getGradients(sensor);
          if (gradients != null)
          {
            DataMessage basePacket = new DataMessage(getAddress(), null, sensor);
            m_sourcePackets++;
            for (Iterator<Gradient> it = gradients.iterator(); it.hasNext();)
            {
              Gradient gradient = it.next();

              DataMessage packet = new DataMessage(basePacket, getAddress(),
                  gradient.getAddress());
              log("Sending " + packet);
              sendPacket(packet);

              if (gradient.isExpired())
              {
                it.remove();
                if (gradient == fastestGradient)
                {
                  m_fastestGradient.put(sensor, getFastestGradient(sensor));
                }
              }
            }
            if (gradients.isEmpty())
            {
              removeInterest(sensor);
              m_fastestGradient.remove(sensor);
            }
          }
        }
      }
    }
  }

  public int getSourcePackets()
  {
    return m_sourcePackets;
  }

  @Override
  public char getOpenSymbol()
  {
    return '<';
  }

  @Override
  public char getCloseSymbol()
  {
    return '>';
  }

}
